#include "domemo.h"

sharedMem *joueur_mem; /* structure pour lire en mémoire partagée */
sharedMem *shmAddr; /* retient l'adresse du segment de la mémoire partagée  */
sharedCounter * shmCounterAddr; /* retient l'adresse de la mémoire partagée du compteur de processus lecteurs */
int shmID; /* id de la mémoire partagée */
int shmIDCounter; /* id de la mémoire partagée du compteur de process lecteurs */
int semID; /* id du set de sémaphore utilisé */
int sock;
int ** pionsJoueurs; /* contiendra les pions des autres joueurs */
int nbrPionsParJoueur;
int nbrJoueurs;
int numeroJoueur;
fd_set * ensemble_lecture; /* contient stdin et le socket client */
struct itimerval timer;
struct itimerval old;
static struct sigaction sig;

void domemo(int sck) {

	int istty;

	sig.sa_flags = SA_RESTART;
	sigemptyset(&sig.sa_mask);
	sig.sa_handler = signalHandler;

	SYS(sigaction(SIGALRM, &sig, NULL));

	/* Permer de faire un reset du timer */
	old.it_interval.tv_sec = 0;
	old.it_interval.tv_usec = 0;
	old.it_value.tv_sec = 0;
	old.it_value.tv_usec = 0;

	/* initialisation par defaut des identifiants  en attendant de recevoir ceux du serveur */
	sock = sck;
	/** initialisation de la struct joueur_mem  qui contiendra les donnees lues en memoire partagee */
	joueur_mem = (sharedMem*) malloc(sizeof(sharedMem));

	ensemble_lecture = (fd_set *) malloc(sizeof(fd_set));
	FD_SET(sock, ensemble_lecture);
	FD_SET(STDIN_FILENO, ensemble_lecture);

	/* le jeu commence */
	istty = isatty(0);
	if (istty)
		printf("Demarrage!\n");

	receptionDesRessources();

	afficherPartie();

	debutDePartie();
}

void signalHandler(int signal) {
	if (signal == SIGALRM)
		fprintf(stderr, "Temps ecoule!\n");
}

void receptionDesRessources() {
	message msg; /* structure contenant le message */
	int readBytes;
	int messageNom = 0; /* pour savoir si le serveur a demande au joueur son nom et prenom */
	int nomIntroduit = 0;
	int prenomIntroduit = 0;

	/*************** ATTENTE DE LECTURE DU SEMID  ET DES PIONS ***************/
	/* La fonction recoit les ressources necessaires au jeu en TERMINANT par la
	 * reception des pions ou alors ferme la connexion au serveur et quitte */

	while (1) {
		int selectTest;
		fd_set rfds;
		FD_ZERO(&rfds);
		memcpy(&rfds, ensemble_lecture, sizeof(rfds));

		if ((selectTest = select(sock + 1, &rfds, 0, 0, NULL)) == -1) {
			if (errno == EINTR) {
				/* Le timer pour enregisrer le nom et prenom du joueur s'est ecoule */
				SYS(setitimer(ITIMER_REAL, &old, NULL));
				if (nomIntroduit == 0) {
					/* Attribue au joueur un nom par defaut */
					message msg;
					char nom[LONG_NOM];
					sprintf(nom,"Defaut");
					msg.type = NOM;
					strcpy(msg.content,nom);
					SYS(write(sock,&msg,sizeof(message)));
					nomIntroduit = 1;
					printf("Un nom vous a ete attribue par defaut.\n");
				}
				if (prenomIntroduit == 0) {
					/* Attribue au joueur un prenom par defaut */
					message msg;
					char prenom[LONG_NOM];
					sprintf(prenom,"Joueur");
					msg.type = PRENOM;
					strcpy(msg.content,prenom);
					SYS(write(sock,&msg,sizeof(message)));
					prenomIntroduit = 1;
					printf("Un prenom vous a ete attribue par defaut.\n");
				}
				messageNom = 0;
			}
			continue;
		}

		if (FD_ISSET(STDIN_FILENO, &rfds)) {
			/* Il y a quelque chose à lire sur stdin ! Soit le nom, soit le prenom */
			char nom[MAXLINE];
			scanf("%s", nom);
			if (messageNom) {
				message msgNom, msgPrenom;
				if (nomIntroduit == 0) {
					if (verifierChaine(nom) != 0)
						printf("Incorrect, reintroduisez : \n");
					else {
						msgNom.type = NOM;
						strcpy(msgNom.content,nom);
						SYS(write(sock,&msgNom,sizeof(message)));
						nomIntroduit = 1;
						printf("Votre nom a ete valide !\n");
					}
				} else if (prenomIntroduit == 0) {
					if (verifierChaine(nom) != 0)
						printf("Incorrect, reintroduisez : \n");
					else {
						msgPrenom.type = PRENOM;
						strcpy(msgPrenom.content,nom);
						SYS(write(sock,&msgPrenom,sizeof(message)));
						prenomIntroduit = 1;
						messageNom = 0;
						/* Le prenom a ete envoye, on arrête le timer */
						SYS(setitimer(ITIMER_REAL, &old, NULL));
						printf("Votre prenom a ete valide !\n");
					}
				}
			}
		}

		else if (FD_ISSET(sock, &rfds)) {
			/* Message du serveur ! */
			bzero(&msg, sizeof(message));
			/* on boucle en attendant de recevoir les id des 2 memoires partagees et du semaphoreSetID */
			SYS(readBytes=read(sock,&msg,sizeof(message)));

			if (readBytes != 0) { /** on a lu qqchose du serveur */

				switch (msg.type) {

				case CLOSE: {
					/* le client doit se terminer , on ne peut pas l'accepter pour jouer */
					printf("%s \n", msg.content);
					printf("Deconnexion..... \n");
					SYS(close(sock));
					exit(2);
				}

				case BIENVENUE: {
					printf("%s \n", msg.content);
					break;
				}

				case SEMAPHORE_SET_ID: {
					semID = atoi(msg.content);
					break;

				}

				case SHARED_MEM: {
					shmID = atoi(msg.content);
					break;

				}

				case SHARED_MEM_RC: {
					shmIDCounter = atoi(msg.content);
					break;

				}

				case MESSAGE: {
					printf("%s \n", msg.content);
					break;
				}

				case NUMERO: {
					numeroJoueur = atoi(msg.content);
					break;
				}

				case NOM: {
					printf("%s \n", msg.content);
					messageNom = 1;
					/* On demarre le timer */
					printf("Attention vous n'avez que %d secondes !\n",
							TIMEOUT_CLI);
					timer.it_value.tv_sec = TIMEOUT_CLI;
					SYS(setitimer(ITIMER_REAL, &timer, NULL));
					break;
				}

				case PIONS_RECUS: {
					/* Le joueur a reçu les pions des autres joueurs
					 * Initialisation de la memoire partagee pour recuperer
					 * les informations necessaires : nbJoueurs et nbPionsParJoueur */
					shmAddr = (sharedMem *) shmAttach(shmID, (char*) shmAddr);
					shmCounterAddr = (sharedCounter *) shmAttach(shmIDCounter,
							(char*) shmCounterAddr);

					lecteur(joueur_mem, shmAddr, shmCounterAddr, semID);
					nbrJoueurs = joueur_mem->joueurs;
					nbrPionsParJoueur
					= joueur_mem->repartition[nbrJoueurs - 2][0];

					traiterPionsRecus(msg.content);
					return;
				}
				default:
					break;
				}
			}
		}
	}
}

int verifierChaine(char * chaine) {
	const char * regex = "^[A-Za-z]+$";
	int err;
	regex_t preg;
	err = regcomp(&preg, regex, REG_NOSUB | REG_EXTENDED);
	if (err == 0) {
		return regexec(&preg, chaine, 0, NULL, 0);
	}
	return -1;
}

int verifierPion(char * pion) {
	char *ptr;
	int val;
	val = strtol(pion, &ptr, 10);
	if (*ptr != '\0')
		return -1;
	if (val < 1 || val > 7)
		return -1;
	return 0;
}

void traiterPionsRecus(char * pionsRecus) {
	int i, j, cpt;
	char * pion;

	/* initialise le tableau des pions a 0 */
	pionsJoueurs = (int**) malloc(nbrJoueurs * sizeof(int*));

	for (i = 0; i < nbrJoueurs; i++)
		pionsJoueurs[i] = (int*) malloc(nbrPionsParJoueur * sizeof(int));

	for (i = 0; i < nbrJoueurs; ++i) {
		for (j = 0; j < nbrPionsParJoueur; ++j) {
			pionsJoueurs[i][j] = VIDE;
		}
	}

	/* on connait le nbrPionsParJoueur on peut traiter la chaîne de caractères */
	i = 0;
	j = 0;
	cpt = 1;
	for (pion = strtok(pionsRecus, PION_SEPARATEUR); pion; pion = strtok(NULL,
			PION_SEPARATEUR)) {
		if (i == numeroJoueur) { /* i vaut le numero du joueur, on passe cette boucle */
			if (i == (nbrJoueurs - 1)) /* le joueur est le dernier joueur on peut quitter la boucle */
				break;
			else
				i++;
		}
		pionsJoueurs[i][j++] = atoi(pion);
		if (cpt == nbrPionsParJoueur) {
			/* on passe aux pions d'un autre joueur ! */
			i++;
			cpt = 0;
			j = 0;
		}
		cpt++;
	}

}

void afficherPartie() {
	int i, j;
	int nbPionsTrouves = 0; /* le nombre de pions trouves par le joueur */

	/* maj de la memoire partagee */
	lecteur(joueur_mem, shmAddr, shmCounterAddr, semID);

	for (i = 0; i < nbrPionsParJoueur; ++i) {
		if (joueur_mem->trouver[numeroJoueur][i] != VIDE)
			nbPionsTrouves++;
	}

	printf("%s", SEPARATEUR);
	printf("\nAffichage de la partie :\n");

	printf("\nVous avez %d pions sur %d : ", nbPionsTrouves, nbrPionsParJoueur);
	for (i = 0; i < nbrPionsParJoueur; ++i) {
		if (joueur_mem->trouver[numeroJoueur][i] == VIDE)
			printf("%s", PION_NON_TROUVE);
		else {
			printf("%d", joueur_mem->trouver[numeroJoueur][i]);
		}
		printf(" ");
	}

	printf("\n");

	/* affichage des pions des autres joueurs ainsi que les pions trouves et la table visible */
	printf("\nPions restants des autres joueurs : \n");

	for (i = 0; i < nbrJoueurs; i++) {
		if (i == numeroJoueur) {
			continue;
		}
		printf("Joueur n°%d %s %s : ", i + 1, joueur_mem->prenom[i],
				joueur_mem->nom[i]);
		for (j = 0; j < nbrPionsParJoueur; j++) {
			if (pionsJoueurs[i][j] != VIDE) /* si egal à 0 le pion est trouve on ne l'affiche                                                     pas */
				printf("%d ", pionsJoueurs[i][j]);
		}
		printf("\n");
	}

	printf("\n---------- Pions sur la table ----------\n");

	/* les pions visibles */
	printf("\nPions faces visibles : ");
	for (i = 0; i < MAX_PIONS_PAR_JOUEUR; ++i) {
		int pion = joueur_mem->visible[i];
		if (pion != VIDE)
			printf("%d ", pion);
	}
	printf("\n");
	/* les pions trouves */
	printf("\nPions trouves des autres joueurs : \n");
	for (i = 0; i < nbrJoueurs; i++) {
		if (i == numeroJoueur) {
			continue;
		}
		printf("Joueur n°%d %s %s : ", i + 1, joueur_mem->prenom[i],
				joueur_mem->nom[i]);
		for (j = 0; j < MAX_PIONS_PAR_JOUEUR; ++j) {
			int pion = joueur_mem->trouver[i][j];
			if (pion != VIDE)
				printf("%d ", pion);
		}
		printf("\n");
	}
	printf("\n----------------------------------------\n");
	printf("%s", SEPARATEUR);
}

void debutDePartie() {
	/*********************LA PARTIE PEUT VRAIMENT COMMENCER *******************/

	int readBytes;
	int faireUneProposition = 0; /* Indique si c'est au joueur de proposer un pion */

	while (1) {
		message msg;
		int selectTest;
		fd_set rfds;
		FD_ZERO(&rfds);
		memcpy(&rfds, ensemble_lecture, sizeof(rfds));

		if ((selectTest = select(sock + 1, &rfds, NULL, NULL, NULL)) == -1) {
			if (errno == EINTR) {
				message msg;
				char pion[10];
				/* Le timer pour proposer un pion est ecoule !
				 * Envoie d'un pion avec la valeur VIDE soit 0 que le serveur
				 * ne va pas traiter */
				SYS(setitimer(ITIMER_REAL, &old, NULL));
				sprintf(pion,"%d",VIDE);
				msg.type = PION;
				strcpy(msg.content,pion);
				SYS(write(sock,&msg,sizeof(message)));
			}
			faireUneProposition = 0;
			continue;
		}

		if (FD_ISSET(STDIN_FILENO, &rfds)) {
			/* Il y a quelque chose à lire sur stdin : la valeur du pion propose */
			char str[MAXLINE];
			int pion;
			scanf("%s", str);
			if (faireUneProposition) {
				message msg;
				if (verifierPion(str) != 0) {
					printf(
							"Incorrect, reintroduisez un chiffre compris entre 1 et 7 : \n");
				} else {
					pion = atoi(str);
					msg.type = PION;
					sprintf(msg.content, "%d", pion);
					SYS(write(sock,&msg,sizeof(message)));
					/* Pion envoye on arrête le timer */
					SYS(setitimer(ITIMER_REAL, &old, NULL));
					faireUneProposition = 0;
				}
			}
		}

		else if (FD_ISSET(sock, &rfds)) {
			bzero(&msg, sizeof(message));

			SYS(readBytes=read(sock,&msg,sizeof(message)));

			if (readBytes != 0) { /* Message du serveur ! */

				switch (msg.type) {

				case CLOSE: {
					printf("%s \n", msg.content);
					printf("Deconnexion..... \n");
					quitter();
					break;
				}

				case PION: {
					afficherPartie();
					/* C'est au joueur de proposer un pion */
					printf("%s \n", msg.content);
					printf("\nQuel pion pensez-vous avoir? : \n");
					faireUneProposition = 1;
					printf("Attention vous n'avez que %d secondes !\n",
							TIMEOUT_CLI);
					timer.it_value.tv_sec = TIMEOUT_CLI;
					SYS(setitimer(ITIMER_REAL, &timer, NULL));
					break;
				}

				case TROUVE: {
					printf("%s \n", msg.content);
					break;
				}

				case EFFACER_PION: {
					/* Un autre joueur a trouve un pion, il faut l'effacer du tableau
					 * des pions des autres joueurs */
					effacerPion(msg.content);
					break;
				}

				case GAGNANT: {
					printf("%s \n", msg.content);
					break;
				}

				case MESSAGE: {
					printf("%s \n", msg.content);
					break;
				}

				case RATE: {
					printf("%s \n", msg.content);
					break;
				}

				case GAGNANT_PAR_DEFAUT: {
					printf("%s \n", msg.content);
					quitter();
					break;
				}

				default:
					fprintf(stderr, "Message %d du serveur inconnu : %s",
							msg.type, msg.content);
					break;
				}
			}
		}
	}
}

void effacerPion(char * msg) {

	int joueur, pion, i;
	char * car;

	/* on recupere le numero du joueur et le pion a efface */
	car = strtok(msg, PION_SEPARATEUR);
	joueur = atoi(car);
	car = strtok(NULL, PION_SEPARATEUR);
	pion = atoi(car);

	for (i = 0; i < nbrPionsParJoueur; ++i) {
		if (pionsJoueurs[joueur][i] == pion) { /* pion trouve, on le met à 0 */
			pionsJoueurs[joueur][i] = VIDE;
			break;
		}
	}
}

void quitter() {
	SYS(close(sock));
	exit(2);
}
